

#include "precompiled.h"
#include "engine.h"

#include <QtGui>
#include <QtCore>
//#include <QtOpenGL>
#include <QApplication>

#include <logger/logger.h>
#include "helper.h"

#include <core/entityobject.h>

#include "zones.h"
#include "configstack.h"
#include "gui/windowproxy.h"
#include "helper.h"
#include "tasks.h"
#include "server/httpserver.h"

//#include <qtode/qtode.h>
//#include <gui/scene.h>
//#include <gui/view.h>
//#include <gui/helpers/glextensions.h>

#include <extensions/context.h>
#include <extensions/zoneinterface.h>

#include <helpers/support/environment.h>

#include "script/contextimpl.h"
#include "script/declarativeenvironmentex.h"

#include "gui/commandbar.h"

#include "ui/loggerui.h"
#include "ui/desktopwidget.h"

#include "windowsmanager.h"

#include "plugins.h"
#include "localserver.h"

#include "tasks/declarativetask.h"

#include "ui/tasksui.h"
//#include <QxtCommandOptions>



#include "server/connectionsmanager.h"
#include <log4Qt/log4qt.h>
#include <logger/logger.h>
//Q_IMPORT_PLUGIN(optragraphicssystem)
//Q_IMPORT_PLUGIN(optravnc)

#define HAVE_LOG4QT_DEBUG_ENABLED

#ifdef HAVE_LOG4QT_DEBUG_ENABLED
  /* log4qt configure load */
  #include <log4Qt/propertyconfigurator.h>
#endif

using namespace Optra::Extensions;

class I : public QThread
{
public:
	static void sleep(unsigned long secs) {
		QThread::sleep(secs);
	}
	static void msleep(unsigned long msecs) {
		QThread::msleep(msecs);
	}
	static void usleep(unsigned long usecs) {
		QThread::usleep(usecs);
	}
};

class TaskTest : public Opl::Core::Task
{
private:
    void run(){
        while(!quitNow())
        {
            qDebug() << "Tick";
            I::sleep(5);

        }
    }
};

class EnginePrivate{
public:
    EnginePrivate():
    guiTask(0)
    ,tasksTasks(0)
    {
		settings = new QSettings("optra2.cfg", QSettings::IniFormat);
	}
    ~EnginePrivate() 
	{
		delete settings;
	}

    enum StartupTasks{
        stZoneInstall
    };


    void addStartupTask(StartupTasks st, const QVariant& param)
    {
        startupTasks.insert(st, param);
    }

    QList<QVariant> startupTasksFor(StartupTasks st, bool remove)
    {
        QList<QVariant> res = startupTasks.values(st);
        if (remove)
        {
            startupTasks.remove(st);
        }

        return res;
    }
    QGraphicsScene* scene;
    QGraphicsView* view;
    HttpServer * http;
    Tasks* tasks;
    Zones* zones;
    WindowsManager* windowsManager;
    ContextPtr context;
    Plugins* plugins;
    DeclarativeEnvironmentEx* declarativeEnvironment;
    LocalServer* localserver;
    Opl::Core::Task* guiTask;
    Opl::Core::Task* tasksTasks;
	QSettings* settings;
	Optra::Engine::Server::ConnectionsManager* connectionManager;

	
   // Logger logger;
private:
    QMultiMap<StartupTasks, QVariant> startupTasks;

};

Engine::Engine(Engine *parent) :
    EntityObject(parent), O_PRIVATE_CREATE(Engine)
{
    O_D(Engine);

//    try
//    {
//        PropertyConfigurator::doConfigure("log4cplus.properties");
//        d->logger = Logger::getInstance("optra_logger");
//    }
//    catch (...)
//    {
//        LOG4CPLUS_ERROR(d->logger, "Exception occured while opening log4cplus.properties...");
//    }
//    LOG4CPLUS_INFO(d->logger, "bbbbbb");

}
WindowsManager* Engine::windowsManager()
{
    O_D(Engine);
    return d->windowsManager;
}

QSettings* Engine::settings()
{
	O_D(Engine);
    return d->settings;
}

QGraphicsScene* Engine::scene()
{
    O_D(Engine);
    return d->scene;
}

Zones* Engine::zones()
{
    O_D(Engine);
    return d->zones;
}

Opl::Helpers::Support::Environment* Engine::environment()
{    
    return Opl::Helpers::Support::Environment::instance();
}

DeclarativeEnvironmentEx* Engine::qml()
{
    O_D(Engine);
    return d->declarativeEnvironment;
}

Optra::Engine::Server::ConnectionsManager* Engine::connectionsManager()
{
	O_D(Engine);
	return d->connectionManager;
}

Engine::~Engine()
{
    O_PRIVATE_DELETE;
}

bool Engine::initialize()
{    

	logger()->info("----------------- Engine initialize %1 -----------------", QDate::currentDate().toString());

    /*QxtCommandOptions options;
    options.setParamStyle(QxtCommandOptions::SpaceAndEquals);
    options.addSection("Help");
    options.add("help", tr(""));

    options.addSection("Logger");
    options.add("logger-level", tr("Log level [FATAL | (ERROR|ERR) | WARNING | INFO | DEBUG | TRACE]"), QxtCommandOptions::ValueOptional, 0);
    options.add("logger-file", tr("Log filename"), QxtCommandOptions::ValueOptional, 0);
    options.add("logger-mode", tr("Log mode [APPEND | TRUNCATE]"), QxtCommandOptions::ValueOptional, 0);

    options.addSection("Config");
    options.add("config", tr("Custom config"), QxtCommandOptions::ValueOptional, 1);

    //Zone option
    options.addSection("Zones");
    options.add("zone-install", tr("Install Zone to optra syetem [FilePath]"), QxtCommandOptions::ValueOptional, 2);

    options.parse(QCoreApplication::arguments());
    if(options.count("help") || options.showUnrecognizedWarning()) {
        options.showUsage();
        return false;
    }*/

    O_D(Engine);

	
    logger()->debug("Load config");
	ConfigStack::config()->load();
	

    /*QString configFile;
    if(options.count("config")) {
        configFile = options.value("config").toString();
        ConfigStack::config()->addFile(configFile, false);
    }

    QString logLevel;
    if(options.count("logger-level")) {
        logLevel = options.value("logger-level").toString();
    }else
        logLevel = conf("logger", "level", "info").toString();

    QString logFile;
    if(options.count("logger-file")) {
        logFile = options.value("logger-file").toString();
    }else
        logFile = conf("logger", "file", "optra.log").toString();


    QString logMode;
    if(options.count("logger-mode")) {
        logMode = options.value("logger-mode").toString();
    }else
        logMode = conf("logger", "mode", "append").toString();



*/

    //QString logFile = "optra.log", logLevel = "trace", logMode = "append";
   // Opl::Core::Logger::Initialize(logFile, logLevel, logMode);



    //Process command for startup tasks
/*
    int zoneinstcount = options.count("zone-install");
    LI.Trace() << "Command Param:zone-install(" << zoneinstcount << ")";
    if (zoneinstcount > 0)
    {
        QVariant val = options.value("zone-install");
        LI.Trace() << "Command Param Option:" << val;
        d->addStartupTask(EnginePrivate::stZoneInstall, val);
    }
*/

    Context* ctx = new ContextImpl(QCoreApplication::applicationDirPath());
    d->context = ContextPtr(ctx);

    d->windowsManager = new WindowsManager("display", this);

    d->tasks = new Tasks(this);
    d->zones = new Zones(this);

    d->http = new HttpServer("http", this);

    prepareScenes();
    d->view->setWindowTitle(tr("Optra Desktop System"));
    d->windowsManager->addScene(d->scene);
    d->windowsManager->initialize();



    //QPushButton* button = new QPushButton("Test");
    //LoggerUI * ui = new LoggerUI(0);
   // ui->show();

    d->localserver = new LocalServer;
    d->localserver->start();
    d->scene->addLine(QLineF(QPointF(0, 0), d->scene->sceneRect().bottomRight()));
//    Window* dl = new CommandBar;
//    dl->Opacity(0.8);
//    Gui::WindowProxy* wp = new Gui::WindowProxy(0, Qt::Window);

//    wp->setWidget(dl);
//    wp->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
//    wp->resize(100, 100);
//    wp->setPos(100, 200);
//    d->scene->addItem(wp);
	d->connectionManager = new Optra::Engine::Server::ConnectionsManager;
	Optra::Engine::Server::registerServerProviders();
	d->connectionManager->Start();

    d->view->show();
    QTimer::singleShot(0, d->http, SLOT(start()));


    d->declarativeEnvironment = new DeclarativeEnvironmentEx;

    return true;
}

void Engine::finalize()
{
    O_D(Engine);

    d->zones->finalizeZones();
    d->view->hide();

    delete d->view;
    delete d->http;
    delete d->localserver;
    delete d->plugins;
    delete d->zones;
    delete d->windowsManager;
    delete d->declarativeEnvironment;
	d->settings->beginGroup("aaa");
	d->settings->beginGroup("xxx");
	d->settings->beginGroup("bbb");
	d->settings->setValue("key", QDate::currentDate());
	d->settings->endGroup();
	d->settings->endGroup();
	d->settings->endGroup();
	d->settings->sync();
    ConfigStack::config()->save();
}

Tasks* Engine::tasks()
{
    O_D(Engine);
    return d->tasks;
}

LocalServer* Engine::localServer()
{
    O_D(Engine);
    return d->localserver;
}

int Engine::exec()
{
    O_D(Engine);
    logger()->info("Engine exec");
    DesktopWidget* dw = new DesktopWidget(0);
    dw->show();
    reloadGui();

    d->zones->loadZones();
    d->zones->initializeZones();
    d->plugins = new Plugins;
    d->plugins->load(d->context);
   // tasks()->start(QUrl::fromLocalFile("zones/developing/bin/applications/creator/libcreator.dll"));
   // Zones::ZonesInterfaces interfaces = d->zones->interfaces();
    //for (int i = 0; i < interfaces.count(); i++)
    //foreach(Optra::Extensions::ZoneInterface* zi, interfaces)
    {
//        Optra::Extensions::ZoneInterface* zi = interfaces.at(i);
//        d->plugins->load(zi->context());
    }

    //d->declarativeEnvironment->exec(QUrl::fromLocalFile("resources/ui/logo.qml"));
    //d->odescene->start();
    //d->scene->toggleRenderer();
    //TasksUI* tui = new TasksUI;
    //tui->show();/


//    TaskTest* t = new TaskTest;
//    tasks()->start(t, "Test Task");

    QList<QVariant> st = d->startupTasksFor(EnginePrivate::stZoneInstall, true);
	
    foreach(QVariant v, st)
    {
        LI.Trace() << "Call Install form Zones Manager for" << v.toString();
        d->zones->install(v.toString());
    }

    return 0;
}

void Engine::reloadGui()
{
    O_D(Engine);
    if (d->guiTask != 0)
    {
        QUuid uuid = d->guiTask->unique();
        tasks()->stop(uuid);
    } 


    d->guiTask = tasks()->start(QUrl::fromLocalFile("extensions/gui/gui.oda"));

}

void Engine::reloadTasks()
{
    O_D(Engine);
    if (d->tasksTasks != 0)
    {
        QUuid uuid = d->tasksTasks->unique();
        tasks()->stop(uuid);
    }


    d->tasksTasks = tasks()->start(QUrl::fromLocalFile("extensions/tasks/tasks.oda"));

}

inline bool matchString(const char *extensionString, const char *subString)
{
    int subStringLength = strlen(subString);
    return (strncmp(extensionString, subString, subStringLength) == 0)
            && ((extensionString[subStringLength] == ' ') || (extensionString[subStringLength] == '\0'));
}

//bool necessaryExtensionsSupported()
//{
//    const char *extensionString = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
//    const char *p = extensionString;
//
//    const int GL_EXT_FBO = 1;
//    const int GL_ARB_VS = 2;
//    const int GL_ARB_FS = 4;
//    const int GL_ARB_SO = 8;
//    int extensions = 0;
//
//    while (*p) {
//        if (matchString(p, "GL_EXT_framebuffer_object"))
//            extensions |= GL_EXT_FBO;
//        else if (matchString(p, "GL_ARB_vertex_shader"))
//            extensions |= GL_ARB_VS;
//        else if (matchString(p, "GL_ARB_fragment_shader"))
//            extensions |= GL_ARB_FS;
//        else if (matchString(p, "GL_ARB_shader_objects"))
//            extensions |= GL_ARB_SO;
//        while ((*p != ' ') && (*p != '\0'))
//            ++p;
//        if (*p == ' ')
//            ++p;
//    }
//    return (extensions == 15);
//}

void Engine::prepareScenes()
{
    O_D(Engine);

//    QVector<Opl::Gui::Light> lights;
//    lights << Opl::Gui::Light(QPointF(3.5, 2.5), 1)
//            << Opl::Gui::Light(QPointF(3.5, 6.5), 1)
//            << Opl::Gui::Light(QPointF(1.0, 1.5), 0)
//            << Opl::Gui::Light(QPointF(1.5, 10.5), 0.3);

    QPixmapCache::setCacheLimit(100 * 1024); // 100 MB




      //  QGLWidget *widget = new QGLWidget(QGLFormat(QGL::SampleBuffers));
       // widget->makeCurrent();


    //if (!necessaryExtensionsSupported()) {
    //    QMessageBox::critical(0, "OpenGL features missing",
    //                          "The OpenGL extensions required to run this demo are missing.\n"
    //                          "The program will now exit.");
    //    delete widget;
    //    return ;
    //}

    //// Check if all the necessary functions are resolved.
    //if (!getGLExtensionFunctions().resolve(widget->context())) {
    //    QMessageBox::critical(0, "OpenGL features missing",
    //                          "Failed to resolve OpenGL functions required to run this demo.\n"
    //                          "The program will now exit.");
    //    delete widget;
    //    return;
    //}

    d->scene = new QGraphicsScene(0, 0, 500, 500);
    d->scene->setStickyFocus(true);

    //d->odescene = new QtOde(this);

    d->view = new QGraphicsView();
    d->view->setScene(d->scene);
    //d->view->resize(800, 600);
//    d->view->setViewport(widget);
    d->view->setRenderHints(d->view->renderHints() | QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    d->view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    //QPixmap background = QPixmap(":/images/background");
   // d->view->setBackgroundBrush(background.scaled(d->view->size()));
}

Engine* Engine::instance()
{
    static Engine engine;//QSharedPointer<Engine> e(new Engine);
    /*if (engine == 0)
        engine = new Engine(0);*/
    return &engine;
}

void myMessageOutput(QtMsgType type, const char *msg)
 {
     //in this function, you can write the message to any stream!
     switch (type) {
     case QtDebugMsg:
         Log4Qt::Logger::logger("Optra")->debug(msg);
         break;
     case QtWarningMsg:
        Log4Qt::Logger::logger("Optra")->warn(msg);
         break;
     case QtCriticalMsg:
		 Log4Qt::Logger::logger("Optra")->error( msg);
         break;
     case QtFatalMsg:
         Log4Qt::Logger::logger("Optra")->fatal(msg);
         abort();
     }
 }

extern "C" EXPORTED int optra_entry(int argc, char **argv)
{
	qInstallMsgHandler(myMessageOutput);

    QApplication app(argc, argv);
    app.setOrganizationName("Optra System");
    app.setApplicationName("Optra");
    QCoreApplication::setOrganizationDomain("optra.org");

#ifdef HAVE_LOG4QT_DEBUG_ENABLED
    /*FIXME: real log4qt configuration path */
    Log4Qt::PropertyConfigurator::configure(
            app.applicationDirPath() + "/log4qt.properties");
#endif
	
	Log4Qt::Logger::logger("Optra")->info("------------------------Start : %1------------------------", QDate::currentDate().toString());
    if (!Engine::instance()->initialize())
	{
		Log4Qt::Logger::logger("Optra")->fatal("initialize failed");
        return -1;
	}

    Engine::instance()->exec();

    int ret = app.exec();
    Engine::instance()->finalize();
	Log4Qt::Logger::logger("Optra")->info("Return code : %1", ret);
	Log4Qt::Logger::logger("Optra")->info("-------------------------End : %1------------------------", QDate::currentDate().toString());
    return ret;
}

int main(int argc, char **argv) { return optra_entry(argc, argv); }
